#include "dce.h"
#include "../../include/cfg.h"
#include "../alias_analysis/alias_analysis.h"
#include "../alias_analysis/simple_alias_analysis.cc"

// 根据函数名查找相应的cfg
extern std::map<std::string, CFG *> CFGMap;
// 声明全局的AliasAnalyser类型的指针alias_analyser，用于调用别名分析相关的功能函数
extern AliasAnalyser *alias_analyser;


void DcePass::Execute() {
    for (auto [defI, cfg] : llvmIR->llvm_cfg) {
        //std::cout<<"execute"<<std::endl;
        SimpleDCE(cfg);
    }
}
// 辅助函数，用于获取指令操作数中寄存器操作数对应的寄存器编号集合
std::set<int> getRegOperandNos(Instruction ins) {
    std::set<int> regNos;
    for (auto op : ins->GetNonResultOperands()) {
        if (op->GetOperandType() == BasicOperand::REG) {
            regNos.insert(((RegOperand *)op)->GetRegNo());
        }
    }
    return regNos;
}

void DcePass::SimpleDCE(CFG *C) {
    // 每个产生结果的寄存器对应的生成该结果的指令
    std::map<int, Instruction> ResultMap;
    // 存储需要进一步检查是否为死代码的寄存器编号。
    // 初始时，只要指令有结果寄存器（即GetResultRegNo返回值不为 -1），其对应的寄存器编号就会被放入WorkList中，
    // 后续不断从这个集合中取出寄存器编号，分析对应的指令是否真的是死代码，起到待检查任务队列的作用。
    std::set<int> WorkList;
    std::map<int, int> RegUseCount;
    std::set<Instruction> needEraseInsSet;

    //遍历每一个基本块，统计指令结果对应的寄存器情况以及各寄存器的使用次数
    for (auto [bid, block] : *C->block_map) {
        //遍历这个快中的所有指令
        for (auto Ins : block->Instruction_list) {
            int v = Ins->GetResultRegNo();
            if (v != -1) {    //这个指令是有result reg的
                WorkList.insert(v);
                ResultMap[v] = Ins;
            }
            for (auto op : Ins->GetNonResultOperands()) {//获得指令中的所有操作数
                if (op->GetOperandType() == BasicOperand::REG) {
                    RegUseCount[((RegOperand *)op)->GetRegNo()]++;
                }
            }
        }
    }

    while (!WorkList.empty()) {
        int v = *WorkList.begin();
        WorkList.erase(v);
        //如果该result的使用次数为0，就要判断它是不是死代码
        //std::cout<<"RegUseCount[v]:"<<v<<" "<<RegUseCount[v]<<std::endl;
        if (RegUseCount[v] == 0) {
            auto resultIns = ResultMap[v];//产生它的那条指令
            // 如果resultI为空指针，有可能是某些特殊类型的指令（比如函数定义相关指令等情况），不处理
            if (resultIns != nullptr) {
                //函数调用，要判断它是没有副作用的
                if (resultIns->GetOpcode() == BasicInstruction::CALL) { 
                    //std::cout<<"函数调用call"<<std::endl; 
                    auto callIns = (CallInstruction *)resultIns;
                    //找到该函数对应的cfg
                    if (CFGMap.find(callIns->GetFunctionName()) == CFGMap.end()) {
                        continue;
                    }
                    auto target_cfg = CFGMap[callIns->GetFunctionName()];
                    //std::cout<<"找到cfg"<<std::endl; 
                    if (!alias_analyser->CFG_isNoSideEffect(target_cfg)) {
                        //std::cout<<"有副作用"<<std::endl;
                        continue;//函数调用有副作用，不能简单地将其当作死代码删除
                    } 
                }
                //到这里就说明这条指令是死代码
                //std::cout<<"是死代码"<<std::endl;
                needEraseInsSet.insert(resultIns);
                //这条指令删除了，指令使用的操作数也要相应减一
//Reference: https://github.com/yuhuifishash/SysY/blob/master/optimize/redundancy_elimination/simple_dce.cc line26
//这里参考在instruction.h里面加入GetNonResultOperands，更方便
                for (auto op : resultIns->GetNonResultOperands()) {
                    if (op->GetOperandType() == BasicOperand::REG) {
                        int x = ((RegOperand *)op)->GetRegNo();
                        RegUseCount[x]--;
                        WorkList.insert(x);//继续检查它是不是死代码，因为可能正好这条删除了，它后面就没有使用了
                    }
                }
            }    
            
        }
    }

    
    //处理需要删除的指令
    for (auto [bid, block] : *C->block_map) {
        auto tmp_Instruction_list = block->Instruction_list;
        block->Instruction_list.clear();
        //遍历这个块的全部指令
        for (auto Ins : tmp_Instruction_list) {
            if (needEraseInsSet.find(Ins) == needEraseInsSet.end()) {
                block->InsertInstruction(1, Ins);//如果这个指令在needEraseInsSet，就是需要删除的
            }
            
        }
    }
}
